package kef

import (
	"errors"
	"fmt"
	"github.com/go-playground/validator/v10"
	"github.com/mitchellh/mapstructure"
	"reflect"
	"strconv"
)

// Request 绑定当前参数到表单请求结构
func (t *Context) Request(formRequest interface{}) error {
	if err := t.scanValue(formRequest, t.Params); err != nil {
		return err
	}
	validate := validator.New()
	err := validate.Struct(formRequest)
	if err == nil {
		return nil
	}
	validationErrors := err.(validator.ValidationErrors)
	return validationErrors
}

func (t *Context) valueOf(val interface{}) reflect.Value {
	res := reflect.ValueOf(val)
	return reflect.Indirect(res)
}

func (t *Context) typeOf(val interface{}) reflect.Type {
	res := reflect.TypeOf(val)
	if res.Kind() == reflect.Ptr {
		res = res.Elem()
	}
	return res
}

func (t *Context) getFieldName(field reflect.StructField) string {
	name := field.Tag.Get("json")
	if name != "" {
		return name
	}
	return field.Name
}

func (t *Context) scanValue(oldValue interface{}, newValue interface{}) error {
	refOldValue := t.valueOf(oldValue)
	refOldType := t.typeOf(oldValue)
	refNewValue := t.valueOf(newValue)
	// refNewType := t.typeOf(newValue)
	switch refOldValue.Kind() {
	case reflect.Map:
		_ = mapstructure.Decode(newValue, oldValue)
	case reflect.Slice, reflect.Array:
		list := reflect.MakeSlice(refOldType, 0, 0)
		paramValue := reflect.ValueOf(newValue)
		for i := 0; i < paramValue.Len(); i++ {
			newVal := reflect.New(refOldType.Elem())
			if err := t.scanValue(newVal.Interface(), paramValue.Index(i).Interface()); err != nil {
				return err
			}
			list = reflect.Append(list, newVal.Elem())
		}
		refOldValue.Set(list)
	case reflect.Uint, reflect.Uint64:
		str := fmt.Sprintf("%v", refNewValue.Interface())
		res, _ := strconv.ParseUint(str, 0, 10)
		refOldValue.SetUint(res)
	case reflect.Int, reflect.Int64:
		str := fmt.Sprintf("%v", refNewValue.Interface())
		res, _ := strconv.ParseInt(str, 0, 10)
		refOldValue.SetInt(res)
	case reflect.Float64:
		str := fmt.Sprintf("%v", refNewValue.Interface())
		res, _ := strconv.ParseFloat(str, 10)
		refOldValue.SetFloat(res)
	case reflect.String:
		refOldValue.SetString(fmt.Sprintf("%v", refNewValue.Interface()))
	case reflect.Bool:
		switch refNewValue.Kind() {
		case reflect.Bool:
			refOldValue.SetBool(refNewValue.Bool())
		case reflect.Int:
			refOldValue.SetBool(refNewValue.Int() == 1)
		case reflect.String:
			str := refNewValue.String()
			refOldValue.SetBool(str == "on" || str == "yes")
		}
	case reflect.Struct:
		numField := refOldType.NumField()

		switch refNewValue.Kind() {
		case reflect.Slice, reflect.Array:
			if numField != refNewValue.Len() {
				return errors.New("参数匹配错误")
			}
			for i := 0; i < numField; i++ {
				tmpValue := reflect.New(refOldType.Field(i).Type)
				if err := t.scanValue(tmpValue.Interface(), refNewValue.Index(i).Interface()); err != nil {
					return err
				}
				refOldValue.Field(i).Set(tmpValue.Elem())
			}
		case reflect.Map:
			for i := 0; i < numField; i++ {
				field := refOldType.Field(i)
				fieldName := t.getFieldName(field)
				tmpValue := reflect.New(field.Type)

				mapValue := refNewValue.MapIndex(reflect.ValueOf(fieldName))
				if reflect.Invalid == mapValue.Kind() {
					continue
				}
				if err := t.scanValue(tmpValue.Interface(), mapValue.Interface()); err != nil {
					return err
				}
				refOldValue.Field(i).Set(tmpValue.Elem())
			}
		}
	}
	return nil
}
